﻿// 604 路径上的最小点权.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <algorithm>
#include <vector>
#include <memory.h>

using  namespace std;
/*
http://oj.daimayuan.top/course/22/problem/980


给你一棵有 n
 个节点的树，节点编号从 1
 到 n
，每个节点有一个点权 ai
。

有 q
 次询问，每次询问读入两个整数 u,v
，请问从 u
 号点到 v
 号点的路径上的点的最小点权是多少。

输入格式
第一行两个整数 n,q
，表示顶点数和询问数。

第二行 n
 个整数 a1,a2,...,an
。

接下来 n−1
 行，每行两个整数 u,v
，表示 u
 号点和 v
 号点之间有一条边。

接下来 q
 行，每行两个整数 u,v
 代表一次询问。

输出格式
对于每个询问输出一行一个整数表示答案。

样例输入
3 3
1 2 3
1 2
2 3
1 2
1 3
2 3
样例输出
1
1
2
数据规模
对于 100%
 的数据，保证 1≤n,q≤105,1≤ai≤105,1≤u,v≤n,u≠v
，数据保证读入是一棵树。
*/

int n, q, a[100001], dist[100001], f[100001][21],v[100001][21];
vector<int> edge[100001];

void dfs(int x) {
	for (auto y : edge[x])
		if (!dist[y]) {
			dist[y] = dist[x] + 1;
			f[y][0] = x;
			dfs(y);
		}
}

int main()
{
	scanf("%d%d",&n,&q);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);

	for (int i = 1; i < n; i++) {
		int x, y;
		scanf("%d%d",&x,&y);
		edge[x].push_back(y);
		edge[y].push_back(x);
	}

	memset(dist, 0, sizeof dist);

	dist[1] = 1;
	dfs(1);
	memset(v, 127, sizeof v);
	for (int i = 1; i <= n; i++)
		v[i][0] = a[i];
	for (int i = 1; i <= 20; i++)
		for (int j = 1; j <= n; j++) {
			f[j][i] = f[f[j][i - 1]][i - 1];
			v[j][i] = min(v[j][i - 1], v[f[j][i - 1]][i - 1]);
		}

	for (; q--; ) {
		int x, y;
		scanf("%d%d", &x, &y);
		if (dist[x] < dist[y])
			swap(x, y);
		int z = dist[x] - dist[y];
		int ans = 1 << 30;
		for (int i = 0; z; z /= 2, ++i)
			if (z & 1){
				ans = min(ans, v[x][i]);
				x = f[x][i];
			}
			if (x != y) {
				for(int i =20;i>=0;--i)
					if (f[x][i] != f[y][i]) {
						ans = min(ans, v[x][i]);
						ans = min(ans, v[y][i]);
						x = f[x][i];
						y = f[y][i];
					}
				ans = min(ans, v[x][0]);
				ans = min(ans, v[y][0]);
				x = f[x][0];
			}
			ans = min(ans, v[x][0]);
			printf("%d\n", ans);
	}
	return 0;
}

 